package com.piece.file.client;

import com.piece.core.framework.enums.FileType;
import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.exception.FrameWorkException;
import com.piece.core.framework.util.basic.SpringUtil;
import com.piece.core.framework.util.collection.MapUtil;
import com.piece.core.framework.util.file.FileUtil;
import com.piece.core.framework.util.file.ImageUtil;
import com.piece.core.framework.util.math.SequenceUtil;
import com.piece.core.framework.util.security.coder.MD5Coder;
import com.piece.core.log.config.UserContextHolder;
import com.piece.file.model.TbSysFile;
import com.piece.file.service.TbSysFileService;
import com.piece.module.file.constants.FileExceptionConstants;
import com.piece.module.file.util.convert.FileConvert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * 文件客户端的抽象类
 */
@Slf4j
public abstract class AbstractFileClient <Config extends FileClientConfig> implements FileClient {

    /**
     * 客户端名称
     */
    private final String clientName;

    /**
     * 文件配置
     */
    protected Config config;

    protected TbSysFileService tbSysFileService;

    public AbstractFileClient(String name, Config config) {
        this.clientName = name;
        this.config = config;
    }

    @Override
    public String getName() {
        return this.clientName;
    }

    /**
     * 初始化
     */
    public final void init() {
        doInit();
        this.tbSysFileService = SpringUtil.getBean(TbSysFileService.class);
        log.info("[文件服务配置({}) 初始化完成]", config);
    }

    /**
     * 自定义初始化
     */
    protected abstract void doInit();

    /**
     * 刷新客户端配置
     */
    public final void refresh(Config config) {
        // 判断是否更新
        if (config.equals(this.config)) {
            return;
        }
        log.info("[文件服务配置({})发生变化，重新初始化]", config);
        this.config = config;
        // 初始化
        this.init();
    }

    protected void validateFile(MultipartFile file) {
        if (null == file || file.isEmpty()) {
            throw new FrameWorkException(new ErrorCode(FileExceptionConstants.EMPTY, (Object[]) null));
        }

        String fileName = file.getOriginalFilename();
        if (!checkFileType(fileName)) {
            throw new FrameWorkException(new ErrorCode(FileExceptionConstants.FORMAT, FileUtil.getFileSuffix(fileName)));
        }
    }

    protected boolean checkFileType(String fileName) {
        return true;
    }

    /**
     * 存储文件
     *
     * @param file       原始文件
     * @param basePath   系统文件存储路径
     */
    @Override
    public TbSysFile store(MultipartFile file, String basePath) throws Exception {
        validateFile(file);
        return writeToStore(file.getBytes(), basePath, file.getOriginalFilename(), file.getSize());
    }

    /**
     * 存储压缩图片
     *
     * @param file       原始文件
     * @param basePath   系统文件存储路径
     * @param maxWidth   图片最大长度
     */
    @Override
    public TbSysFile storeScale(MultipartFile file, String basePath, int maxWidth) throws Exception {
        validateFile(file);
        byte[] bytes = ImageUtil.scale(file.getInputStream(), maxWidth);
        return writeToStore(bytes, basePath, file.getOriginalFilename(), file.getSize());
    }

    /**
     * 存储压缩图片
     *
     * @param file       原始文件
     * @param basePath   系统文件存储路径
     * @param width      图片最大宽度
     * @param height     图片最大高度
     */
    @Override
    public TbSysFile storeScale(MultipartFile file, String basePath, int width, int height) throws Exception {
        validateFile(file);
        byte[] bytes = ImageUtil.screenShot(file.getOriginalFilename(), file.getInputStream(), width, height);
        return writeToStore(bytes, basePath, file.getOriginalFilename(), file.getSize());
    }

    /**
     * 储存文件
     *
     * @param bytes
     * @param storePath
     * @param originalFilename
     */
    @Override
    public TbSysFile writeToStore(byte[] bytes, String storePath, String originalFilename, long fileSize) throws Exception {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String originalName = FileUtil.getFilePrefix(originalFilename);
        String extName = FileUtil.getFileSuffix(originalFilename).toLowerCase();
        String serverName = FileUtil.generateUniqFileName(request) + "." + extName;

        // 文件入库
        String md5 = MD5Coder.md5File(bytes);
        TbSysFile file = writeToServerStore(bytes, storePath, serverName, md5);
        file.setFileId(SequenceUtil.uuid());
        file.setMd5(md5);
        file.setOriginalFilename(originalName + "." + extName);
        file.setContentType(extName);
        file.setFileSize(fileSize);
        file.setDuration(0L);

        if (FileUtil.checkImageType(originalFilename)) {
            file.setChannel(FileType.IMAGE.ordinal() + "");
        } else if (FileUtil.checkOfficeType(originalFilename) || FileUtil.checkPdfType(originalFilename)) {
            file.setChannel(FileType.DOCUMENT.ordinal() + "");
        } else if (FileUtil.checkAudioType(originalFilename)) {
            file.setChannel(FileType.MUSIC.ordinal() + "");
            long duration = FileConvert.parseDuration(originalName, bytes);
            file.setDuration(duration);
        } else if (FileUtil.checkVideoType(originalFilename)) {
            file.setChannel(FileType.VIDEO.ordinal() + "");
            long duration = FileConvert.parseDuration(originalName, bytes);
            file.setDuration(duration);
        } else {
            file.setChannel(FileType.OTHER.ordinal() + "");
        }
        file.setUploadTime(new Date());
        file.setCreateBy(UserContextHolder.getUserId());
        return saveTbSysFile(file);
    }

    public String writeToLocalStore(byte[] bytes, String path, String name) throws IOException {
        String storePathAndName = path + File.separator + name;
        FileUtil.writeByteArrayToFile(new File(storePathAndName), bytes);
        return storePathAndName;
    }

    /**
     * 写入文件
     *
     */
    public TbSysFile writeToServerStore(byte[] bytes, String path, String name, String md5) throws Exception {
        return writeToServerStore(bytes, path, name, md5, true);
    }

    /**
     * 写入文件
     *
     * @param bytes
     * @param path   文件目录
     * @param name   文件名称
     * @param stat   是否使用系统配置位置
     */
    public abstract TbSysFile writeToServerStore(byte[] bytes, String path, String name, String md5, boolean stat) throws Exception;

    public TbSysFile saveTbSysFile(TbSysFile file) throws Exception {
        return tbSysFileService.insert(file);
    }

    public void updateTbSysFile(TbSysFile file) throws Exception {
        tbSysFileService.update(file);
    }

    public void deleteTbSysFile(TbSysFile file) throws Exception {
        tbSysFileService.delete(file.getFileId());
    }

    /**
     * 检查是否存在相同md5值的文件
     * @param file
     * @return
     */
    public boolean checkMd5(TbSysFile file) {
        List<TbSysFile> files = tbSysFileService.findByCondition(MapUtil.builder(new HashMap<String, Object>())
                .put("Q_fileId_S_NEQ", file.getFileId())
                .put("Q_md5_S_EQ", file.getMd5())
                .build());
        return files.size() > 0;
    }
}
